package Model.TransFormModel;


import Model.SurroundModel.Ship;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

public class Sampling {
    public ArrayList<Ship> ships = new ArrayList<Ship>();//建立Ship对象保存图片转化成二值图片各像素点的值
    public static ArrayList<Ship> correctShip = new ArrayList<Ship>();//修正船，用于保存修改后的图片像素点坐标
    private static int SW = 167;//二值图像的阈值（这里根据测试设置为167，可根据实际图像情况进行更改）
    public int MOVE = 100;//图像坐标平移距离
    private int a = 0;
    private static int b = 0;//记一下船的编号

    public static int getSW() {
        return SW;
    }

    public static void setSW(int SW) {
        Sampling.SW = SW;
    }

    //默认构造函数
    public Sampling() {
    }

    //设定阈值构造函数
    public Sampling(int MOVE) {
        this.MOVE = MOVE;
    }

    //设定阈值与坐标移动距离的构造函数
    public Sampling(int SW, int MOVE) {
        this.SW = SW;
        this.MOVE = MOVE;
    }

    //将图片转换为二值图片

    /**
     * 转换
     *
     * @param srcPath   原图片url地址
     * @param alterPath 保存图片url地址
     */
    public void transfer(String srcPath, String alterPath) throws IOException {
        BufferedImage bi = ImageIO.read(new File(srcPath));//通过imageio将图像载入
        int h = bi.getHeight();//获取图像的高
        int w = bi.getWidth();//获取图像的宽
        int rgb = bi.getRGB(0, 0);//获取指定坐标的ARGB的像素值
        int[][] gray = new int[w][h];//用于保存每个像素的灰度值

        //循环获取每个像素点的灰度值
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                gray[x][y] = getGray(bi.getRGB(x, y));
            }
        }

        //创建BufferedImage对象保存转换成二值的图像
        BufferedImage nbi = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
        //对图像每个像素点进行灰度值与阈值判断，大于阈值的RGB均设为255，小于阈值的RGB均设为0
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                a++;
                if (getAverageColor(gray, x, y, w, h) > SW) {
                    int max = new Color(255, 255, 255).getRGB();
                    nbi.setRGB(x, y, max);
                } else {
                    int min = new Color(0, 0, 0).getRGB();
                    ships.add(new Ship(a, x, y));
                    nbi.setRGB(x, y, min);
                }
                //System.out.println("二进制图像坐标：" + "(" + x + "," + y + ")");
            }
        }

        //获取船的数量
        System.out.println("船的数量为：" + ships.size());

        //绘制二值图像保存到本地
        ImageIO.write(nbi, "jpg", new File(alterPath));
    }

    //通过计算每个像素点的RGB值取平均值作为该像素点的灰度值
    public static int getGray(int rgb) {
        String str = Integer.toHexString(rgb);
        Color c = new Color(rgb);
        int r = c.getRed();
        int g = c.getGreen();
        int b = c.getBlue();
        int top = (r + g + b) / 3;
        return top;
    }

    //自己加周围8个灰度值再除以9，算出其相对灰度值
    public static int getAverageColor(int[][] gray, int x, int y, int w, int h) {
        int rs = gray[x][y]
                + (x == 0 ? 255 : gray[x - 1][y])
                + (x == 0 || y == 0 ? 255 : gray[x - 1][y - 1])
                + (x == 0 || y == h - 1 ? 255 : gray[x - 1][y + 1])
                + (y == 0 ? 255 : gray[x][y - 1])
                + (y == h - 1 ? 255 : gray[x][y + 1])
                + (x == w - 1 ? 255 : gray[x + 1][y])
                + (x == w - 1 || y == 0 ? 255 : gray[x + 1][y - 1])
                + (x == w - 1 || y == h - 1 ? 255 : gray[x + 1][y + 1]);
        return rs / 9;
    }

    //根据保存的坐标点用java绘制

    /**
     * 放大图片
     *
     * @param g2d Graphics2D参数
     * @param s   需要绘制的对象
     */
    public static void draw(Graphics2D g2d, ArrayList<Ship> ship, Sampling s) {
        //将保存的坐标点绘制
        for (int i = 0; i < ship.size(); i++) {
            //System.out.println("二进制图像坐标：" + "(" + ships.get(i).x + "," + ships.get(i).y + ")");
            g2d.draw(new Line2D.Double(ship.get(i).x * 2 + s.MOVE, ship.get(i).y * 2 + s.MOVE,
                    ship.get(i).x * 2 + s.MOVE + 0.5, ship.get(i).y * 2 + s.MOVE + 0.5));
            //g2d.draw(new Ellipse2D.Double(ships.get(i).x+100,ships.get(i).y+100,0.1,0.1));
        }
    }

    //按照另一图片像素点个数放大图片

    /**
     * 放大图片
     *
     * @param srcPath 源二值图片
     * @param multple 放大的倍数
     * @param newSize 要变换图片的像素点个数
     */
    public static void zoomInImage(String srcPath, int multple, int newSize) throws IOException {
        BufferedImage bi = ImageIO.read(new File((srcPath))); //读入源二值图片

        // 获取图片的宽度和高度
        int h = bi.getHeight();//获取图像的高
        int w = bi.getWidth();//获取图像的宽
        int[][] gray = new int[w][h];//用于保存每个像素的灰度值
        int x = 0;//像素点x坐标
        int y = 0;//像素点y坐标
        ArrayList<Color> colorList = new ArrayList<>();// 保存像素列表

        // 存放新图片
        BufferedImage nbi = new BufferedImage(w * multple, h * multple, BufferedImage.TYPE_BYTE_BINARY);

        //循环获取每个像素点的灰度值
        for (x = 0; x < w; x++) {
            for (y = 0; y < h; y++) {
                gray[x][y] = getGray(bi.getRGB(x, y));
            }
        }

        for (x = 0; x < w; x++) {
            for (y = 0; y < h; y++) {
                if (getAverageColor(gray, x, y, w, h) > SW)  //重新获取每个像素点的平均像素值，并与阈值进行比较
                    colorList.add(Color.white); //大于阈值则将该像素点赋予白色
                else {
                    colorList.add(Color.black); //小于阈值则将该像素点赋予黑色
                    //correctShip.add(new Ship(x,y));
                }
            }
        }

        // 将像素表设置好相应的位置
        int m = 0;
        int n = 0;
        int k = 0;
        Color color = null;

        //continueOut用于退出最外层循环
        continueOut:
        for (x = 0; x < w * multple; x += multple) {
            for (y = 0; y < h * multple; y += multple) {
                color = colorList.get(k++);

                // 当所有像素都已经保存好, 写入到新图片
                if (k >= colorList.size()) {
                    //ImageIO.write(nbi, "jpg", new File("D:二值化后_放大.jpg"));
                    //ImagePixel.writeImage(wi, newPath);
                    break continueOut;
                }

                // 扩大的像素
                for (m = 0; m < multple; ++m) {
                    for (n = 0; n < multple; ++n) {
                        if (color == Color.black) {
                            b++;
                            nbi.setRGB(x + m, y + n, new Color(0, 0, 0).getRGB());
                            correctShip.add(new Ship(b, x, y)); //将黑色像素点保存到修正数组中
                        } else
                            nbi.setRGB(x + m, y + n, new Color(255, 255, 255).getRGB());
                    }
                }
            }
        }
        System.out.println(correctShip.size());

        int i = 0;
        //循环对放大图片进行栅格操作
        while (correctShip.size() > newSize) {
            correctShip.remove(i);
            nbi.setRGB((int) correctShip.get(i).x, (int) correctShip.get(i).y, new Color(255, 255, 255).getRGB());
            i = (i + 4) % (correctShip.size() - 1); //每隔四个像素点栅格一次
        }
        ImageIO.write(nbi, "jpg", new File(srcPath));
        System.out.println(correctShip.size());
    }


    public ArrayList<Ship> appointShip = new ArrayList<Ship>(); //指定船的数量，并保存各像素点坐标
    public static int count = 1;
    //按照另一图片像素点个数放大图片

    /**
     * 放大图片
     *
     * @param srcPath   源二值图片
     * @param alterPath 修改后二值图片存放的url
     * @param amount    船只个数
     */
    public void alterAmount(String srcPath, String alterPath, int amount) throws IOException {
        BufferedImage bi = ImageIO.read(new File((srcPath))); //读入源二值图片

        transfer(srcPath, "D:/二值化后_无压缩" + count + ".jpg");
        count++;
        this.appointShip = this.ships;

        int i = 0;
        //循环对图片进行栅格操作
        while (this.appointShip.size() > amount) {
            this.appointShip.remove(i);
            bi.setRGB((int) this.appointShip.get(i).x, (int) this.appointShip.get(i).y, new Color(255, 255, 255).getRGB());
            i = (i + 4) % (this.appointShip.size() - 1); //每隔四个像素点栅格一次
        }

        ImageIO.write(bi, "jpg", new File(alterPath));
        System.out.println("修改后图片大小为：" + this.appointShip.size());
    }


    //按照船的数量，重新给船队数组编号
    public void ResetId() {
        for (int i = 0; i < this.appointShip.size(); i++) {
            Ship s = this.appointShip.get(i);
            s.num = i;
            this.appointShip.set(i, s);
        }
    }

}